package devbureau.fstore.common.print.invoice;

import devbureau.fstore.common.Currency;
import devbureau.fstore.common.CustomerData;
import devbureau.fstore.common.CustomerDataImpl;
import devbureau.fstore.common.ItemData;
import devbureau.fstore.common.OrderData;
import devbureau.fstore.common.OrderLine;
import devbureau.fstore.common.OrderLineData;
import devbureau.fstore.common.print.PrintException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.CellRangeAddress;

import static devbureau.fstore.util.ExcelHelper.*;
import static devbureau.fstore.util.Utilities.Formatting.formatAsCurrency;
/**
 *This class creates invoice xls file using template stored in jar.
 * Do not modify template.
 * @author ssheipak
 */
public class PrintInvoice {
    private static final Logger log = Logger.getLogger(PrintInvoice.class);
    private static final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");


    //anchors for template
    public static final short CUSTOMER_ADDR_AND_DATE = 3;    //POI counts starting from "0". Excel counts from "1"
    public static final short WAY_BILL_DATA = 5;    //way_bill number and bill date
    public static final short BEGIN_LINES = 8;

    //default values
    private static final String defaultPathToTemplateFile = "waybill_template.xls";
    private static final String defaultPathForStoringFiles = "C:/";

    private final OrderData<OrderLine> order;
    private final Currency currency;
    private final Double rate;
    
    //file stuff
    private String pathToResultDir = null;          //if files storage used
    private String pathToResultFile = null;         //file with result if pathToResultDir is used
    private static final String XLS = ".xls";       //file suffix

    //if streaming
    private OutputStream resultOutStream = null;    //if you want to write to OutputStream directly, for example in Servlet.response
    
    private HSSFCellStyle tableStyle = null;
    private HSSFCellStyle headStyle = null;
    /**Use default values and write result to specified stream
     * file will not be created
     */
    public PrintInvoice(OrderData<OrderLine> order, OutputStream resultStream) throws Exception{
        this(order, resultStream, Currency.RUR, 1D);
    }

   public PrintInvoice(OrderData<OrderLine> order, OutputStream resultStream, Currency currency, Double rate) throws Exception{
        this.order = order;
        this.resultOutStream = resultStream;
        this.rate = rate;
        this.currency = currency;

        createInvoice();
    }



    /**Create invoice and store to desired folder.
     * @param order is an order for which you want to get invoice
     * @param pathToStoreDir is a path to dir where result file will be stored.
     * You'll get absolute path to this file.
     */
    public PrintInvoice(OrderData<OrderLine> order, String pathToStoreDir) throws Exception{
        this.order = order;
        pathToResultDir = pathToStoreDir;
        this.rate = 1D;
        this.currency = Currency.RUR;
        createInvoice();
    }

    /**Takes paid order and template file.
     * @param order is a paid order
     * Result file will be stored to default place
     */
    public PrintInvoice(OrderData<OrderLine> order) throws Exception{
        this.order = order;
        pathToResultDir = defaultPathForStoringFiles;
        this.rate = 1D;
        this.currency = Currency.RUR;

        createInvoice();
    }

    private void createInvoice() throws Exception{
        InputStream xlsIS = null;
        OutputStream out = null;

        try{
            if(order == null){
                throw new PrintException("Заказ не выбран, счет-факутра не может быть создана.");
            }
            xlsIS = this.getClass().getClassLoader().getResourceAsStream(defaultPathToTemplateFile);
            log.debug("CreateInvoiceXLS for order id["+order.getId()+"]");

            HSSFWorkbook workBook = new HSSFWorkbook(xlsIS);
            HSSFSheet sheet = workBook.getSheetAt(0);

            CustomerData customer = prepareCustomerData(order.getCustomer());
            log.debug("CreateInvoiceXLS for CustomerLabel["+customer.getLabel()+"]");

            HSSFCell addrCell = sheet.getRow(CUSTOMER_ADDR_AND_DATE).createCell(1);
            addrCell.setCellStyle(getHeadStyleWithBorder(workBook));
            addrCell.setCellValue(convert("Получатель: " + customer.getLabel() + ", " + customer.getAddress().trim()));
            addrCell.getCellStyle().setWrapText(true);
            for(int i=2; i<=5; i++){
                HSSFCell tmpCell = sheet.getRow(CUSTOMER_ADDR_AND_DATE).createCell(i);
                tmpCell.setCellStyle(getHeadStyleWithBorder(workBook));
                tmpCell.getCellStyle().setWrapText(true);
            }
            CellRangeAddress cellRangeAddr = new CellRangeAddress(CUSTOMER_ADDR_AND_DATE, CUSTOMER_ADDR_AND_DATE, 1, 5);
            sheet.addMergedRegion(cellRangeAddr);
            sheet.getRow(CUSTOMER_ADDR_AND_DATE).setHeight((short)(sheet.getRow(CUSTOMER_ADDR_AND_DATE).getHeight()*2));

            HSSFCell numCell = sheet.getRow(WAY_BILL_DATA).createCell(1);
            numCell.setCellStyle(getHeadStyle(workBook));
            numCell.setCellValue(convert("Накладная № " + order.getId()));


            HSSFCell dateCell = sheet.getRow(WAY_BILL_DATA).createCell(4);
            dateCell.setCellStyle(getHeadStyle(workBook));
            dateCell.setCellValue(convert("От " + sdf.format(new Date()) + " г."));

            List<OrderLineData> lines = order.getLines();

            //shift invoice footer.
            sheet.shiftRows(21, 31, lines.size()+1);
            log.debug("footer shifted.");

            //insert order lines
            Double totalPrice = 0.0D;
            for(int i=0; i<lines.size();i++){
               HSSFRow row = sheet.createRow(BEGIN_LINES+i);
               OrderLineData oLine = lines.get(i);
               ItemData iData = oLine.getItemData();
              
               HSSFCell titleCell = row.createCell(1);
               titleCell.setCellStyle(tableStyleAlignLeft(workBook));
               String title = (iData.getNameRU() == null || iData.getNameRU().isEmpty()) ? iData.getName() : iData.getNameRU();
               titleCell.setCellValue(convert(title));

               HSSFCell cellLength = row.createCell(2);
               cellLength.setCellType(HSSFCell.CELL_TYPE_STRING);
               cellLength.setCellStyle(getTableStyle(workBook));
               cellLength.setCellValue(convert( iData.getLength() == null ? "" : iData.getLength().toString() ));

               HSSFCell cellQtty = row.createCell(3);
               cellQtty.setCellType(HSSFCell.CELL_TYPE_STRING);
               cellQtty.setCellStyle(getTableStyle(workBook));
               cellQtty.setCellValue(convert( oLine.getQuantity().toString() ));

               HSSFCell cellPrice = row.createCell(4);
               cellPrice.setCellType(HSSFCell.CELL_TYPE_STRING);
               cellPrice.setCellStyle(getTableStyle(workBook));
               //cellPrice.setCellValue(convert( String.format("%10.2f"+currency.getSuffix(), oLine.getPrice()/rate) ));
               cellPrice.setCellValue(convert(formatAsCurrency(oLine.getPrice()/rate)+currency.getSuffix()));

               HSSFCell cellRowPrice = row.createCell(5);
               cellRowPrice.setCellType(HSSFCell.CELL_TYPE_STRING);
               cellRowPrice.setCellStyle(getTableStyle(workBook));
               //cellRowPrice.setCellValue(convert( String.format("%10.2f"+currency.getSuffix(), oLine.getPrice()*oLine.getQuantity()/rate) ));
               cellRowPrice.setCellValue(convert( formatAsCurrency(oLine.getPrice()*oLine.getQuantity()/rate) + currency.getSuffix()));

               totalPrice+= oLine.getPrice()*oLine.getQuantity();
            }

            int totalRowNum = lines.size()+8 +1;//8 = header, 1 = just spacing
            HSSFRow totalRow = sheet.createRow(totalRowNum);
            HSSFCell labelCell = totalRow.createCell(4);
            labelCell.setCellStyle(getHeadStyle(workBook));
            labelCell.setCellValue(convert("Итого: "));

            HSSFCell totalValue = totalRow.createCell(5);
            totalValue.setCellStyle(getHeadStyleAlignRight(workBook));
            //totalValue.setCellValue(convert(String.format("%10.2f"+currency.getSuffix(), totalPrice/rate)));
            totalValue.setCellValue(formatAsCurrency(totalPrice/rate)+currency.getSuffix());

            int subscribeRowNum = totalRowNum +3;//3 = spacing
            HSSFRow subscribeRow = sheet.createRow(subscribeRowNum);
            HSSFCell sellerCell = subscribeRow.createCell(1);
            sellerCell.setCellStyle(tableStyleAlignLeftWithMediumBorder(workBook));
            sellerCell.setCellValue(convert("Отпустил: "));

            
            for(int i=4; i<=5; i++){
                HSSFCell customerCell = subscribeRow.createCell(i);
                customerCell.setCellStyle(tableStyleAlignLeftWithMediumBorder(workBook));
                if(i==4){
                    customerCell.setCellValue(convert("Получил"));
                }
            }

            log.debug("lines printed.");
            //write result
            if(pathToResultDir!=null){
                pathToResultFile = pathToResultDir+"invoice_"+order.getId()+XLS;
                out = new FileOutputStream(pathToResultFile);
                workBook.write(out);
                log.debug("file saved here["+pathToResultFile+"]");
            }else if(resultOutStream !=null){
                 workBook.write(resultOutStream);
                 log.debug("XLS has been written to specified outer output stream.");
            }else{
                log.error("Can't determine where to write result. Exception will be thrown");
                throw new PrintException("Ошибка в настройках генератора счетов-фактур. Счет-фактура не создана.");
            }
        }
        catch(PrintException pe){
            log.error("PrintException occured with message["+pe.getMessage()+"]");
            throw new Exception(pe.getMessage());
        }
        catch(Exception e){
            log.error("Error while preparing XLS-invoice for order["+order.getId()+"]", e);
            throw new Exception("Счет-фактура не создана из-за ошибки.");
        }
        finally{
            if(out!=null){
                out.flush();
                out.close();
            }
            if(xlsIS!=null){
                xlsIS.close();
            }
        }
    }

   
    /**This holds path to ready xls invoice.
     * @return path to xls file.
     */
    public String getPathToFile(){
        return pathToResultFile;
    }

    private CustomerData prepareCustomerData(CustomerData src){
        CustomerData customer = null;
        if(src == null){
            customer = new CustomerDataImpl(0, "", "", "", true);
        }else{
            customer = new CustomerDataImpl(src.getId(),
                                            emptyStringOrValue(src.getLabel()),
                                            emptyStringOrValue(src.getINN()),
                                            emptyStringOrValue(src.getAddress()), true);
        }
        return customer;
    }
   
    private HSSFCellStyle getTableStyle(HSSFWorkbook wb){
        if(tableStyle == null){
            HSSFFont arialCyr11 = wb.createFont();
            arialCyr11.setFontHeightInPoints((short)11);
            arialCyr11.setFontName("Arial Cyr");
            tableStyle = wb.createCellStyle();
            tableStyle.setFont(arialCyr11);
            tableStyle.setAlignment(HSSFCellStyle.ALIGN_RIGHT);
            tableStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_BOTTOM);
            setBorders(tableStyle, HSSFCellStyle.BORDER_THIN, HSSFCellStyle.BORDER_THIN, HSSFCellStyle.BORDER_THIN, HSSFCellStyle.BORDER_THIN);
        }
        return tableStyle;
    }

    private HSSFCellStyle getHeadStyle(HSSFWorkbook wb){
        if(headStyle == null){
            HSSFFont arialCyr12 = wb.createFont();
            arialCyr12.setFontHeightInPoints((short)12);
            arialCyr12.setFontName("Arial Cyr");
            headStyle = wb.createCellStyle();
            headStyle.setFont(arialCyr12);
            headStyle.setAlignment(HSSFCellStyle.ALIGN_LEFT);
            headStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_BOTTOM);
        }
        return headStyle;
    }

    //for address
    private HSSFCellStyle getHeadStyleWithBorder(HSSFWorkbook wb){
        HSSFCellStyle style = wb.createCellStyle();
        style.cloneStyleFrom(getHeadStyle(wb));
        style.setBorderBottom(HSSFCellStyle.BORDER_MEDIUM);
        return style;
    }

    private HSSFCellStyle tableStyleAlignLeft(HSSFWorkbook wb){
        HSSFCellStyle style = wb.createCellStyle();
        style.cloneStyleFrom(getTableStyle(wb));
        style.setAlignment(HSSFCellStyle.ALIGN_LEFT);
        return style;
    }

    //for subscription
    private HSSFCellStyle tableStyleAlignLeftWithMediumBorder(HSSFWorkbook wb){
        HSSFCellStyle style = wb.createCellStyle();
        style.cloneStyleFrom(getTableStyle(wb));
        style.setAlignment(HSSFCellStyle.ALIGN_LEFT);
        setBorders(style, HSSFCellStyle.BORDER_NONE, HSSFCellStyle.BORDER_NONE, HSSFCellStyle.BORDER_NONE, HSSFCellStyle.BORDER_MEDIUM);
        return style;
    }

    private HSSFCellStyle getHeadStyleAlignRight(HSSFWorkbook wb){
        HSSFCellStyle style = wb.createCellStyle();
        style.cloneStyleFrom(getHeadStyle(wb));
        style.setAlignment(HSSFCellStyle.ALIGN_RIGHT);
        return style;
    }

    
}
